#include "stdafx.h"
#include "XMLResponseParser.h"
#include <MsXml2.h>

namespace XML {

DEFINE_LOGGER(CXMLResponseParser)
DEFINE_LOGGER(CXMLResponseParser::XMLDocumentParser)
DEFINE_LOGGER(CXMLResponseParser::TextParser)

CXMLResponseParser::CXMLResponseParser(IDispatch* pDisp)
	: m_pParser(NULL)
{
	Initialize(pDisp);
}

CXMLResponseParser::CXMLResponseParser(const CComBSTR &xml)
	: m_pParser(NULL)
{
	Initialize(xml);
}

CXMLResponseParser::~CXMLResponseParser()
{
	if (m_pParser) delete m_pParser, m_pParser = NULL;
}

bool CXMLResponseParser::Initialize(IDispatch* pDisp)
{
	ATLASSERT(pDisp);

	WEBBrowser browser;
	if (!pDisp || FAILED(pDisp->QueryInterface(&browser)) || !browser)
		return false;

	ATLASSERT(browser);

	if (logger.IsLogEnabled()) LOG("Parsing html:\n", browser.document.body.innerText);

    HTML::Document document(browser.document);
	if (!document) return false;

	CComPtr<IDispatch> dispXmlDoc = document.property[L"XMLDocument"];

	if (dispXmlDoc)
	{
		CComPtr<IXMLDOMDocument2> xmlDoc2;
		if (SUCCEEDED(dispXmlDoc->QueryInterface(IID_IXMLDOMDocument2, (void**)&xmlDoc2)) && xmlDoc2)
			m_pParser = new XMLDocumentParser(xmlDoc2);
		else
		{
			CComPtr<IXMLDOMDocument> xmlDoc;
			if (SUCCEEDED(dispXmlDoc->QueryInterface(IID_IXMLDOMDocument, (void**)&xmlDoc)) && xmlDoc)
				m_pParser = new XMLDocumentParser(xmlDoc);
		}
	}

	if (!m_pParser)
	{
		ATLASSERT(document.body);

		CComBSTR bsBody = document.body.innerText;
		if (!bsBody || CStringW(bsBody).Trim().IsEmpty())
            return false;

		LOG("Parsing html as text:\n", bsBody);

		m_pParser = new TextParser(bsBody);
	}

	return true;
}

bool CXMLResponseParser::Initialize(const CComBSTR &bsXML)
{
	ATLASSERT(bsXML);

	if (!bsXML)
		return false;

	CComPtr<IXMLDOMDocument> document;
	if (SUCCEEDED(document.CoCreateInstance( __uuidof(DOMDocument) )))
	{
		ATLASSERT(document.p != NULL);

		// Load the XML document file...
		VARIANT_BOOL bSuccess = VARIANT_FALSE;
		if (document && SUCCEEDED(document->loadXML(bsXML, &bSuccess)) && VARIANT_TRUE == bSuccess)
			m_pParser = new XMLDocumentParser(document);
	}

	if (!m_pParser)
	{
		LOG("Parsing XML as text:\n", bsXML);

		m_pParser = new TextParser(bsXML);
	}

	return true;
}

CComBSTR CXMLResponseParser::XMLDocumentParser::GetNodeValue(WCHAR* path, WCHAR* node) const
{
	ATLASSERT(m_xmlDocument && path && node);

	WCHAR xpath[255] = L"";
	swprintf_s(xpath, L"%s/%s/text()", path, node);
	ATLTRACE(L"Getting value for: %s\n", xpath);

	CComPtr<IXMLDOMNode> domNode;
	if (FAILED(m_xmlDocument->selectSingleNode(CComBSTR(xpath), &domNode)) || !domNode)
		return CComBSTR();

	ATLASSERT(domNode);

	CComVariant value;
	if (FAILED(domNode->get_nodeValue(&value)) || !(value.vt & VT_BSTR) || !value.bstrVal)
		return CComBSTR();

	ATLASSERT((value.vt & VT_BSTR) && value.bstrVal);

	return value.bstrVal;
}

CComBSTR CXMLResponseParser::XMLDocumentParser::GetNodeAttribute(WCHAR* path, WCHAR* node, WCHAR* attribute) const
{
	ATLASSERT(m_xmlDocument && path && node);

	WCHAR xpath[255] = L"";
	swprintf_s(xpath, L"%s/%s/@%s", path, node, attribute);
	ATLTRACE(L"Getting value for: %s\n", xpath);

	CComPtr<IXMLDOMNode> domNode;
	if (FAILED(m_xmlDocument->selectSingleNode(CComBSTR(xpath), &domNode)) || !domNode)
		return CComBSTR();

	ATLASSERT(domNode);

	CComVariant value;
	if (FAILED(domNode->get_nodeValue(&value)) || !(value.vt & VT_BSTR) || !value.bstrVal)
		return CComBSTR();

	ATLASSERT((value.vt & VT_BSTR) && value.bstrVal);

	return value.bstrVal;
}

CComBSTR CXMLResponseParser::TextParser::GetNodeValue(WCHAR* path, WCHAR* node) const
{
	ATLASSERT(m_bsDocument && node);

	CStringW open = (CStringW)L"<" + node + L">";
	CStringW close = (CStringW)L"</" + node + L">";

	WCHAR* pStart = wcsstr(m_bsDocument, open), *pEnd = wcsstr(m_bsDocument, close);

	if (!pStart || !pEnd)
		return CComBSTR();

	int iLength = pEnd - pStart - open.GetLength();

	CStringW value;
	wcsncpy(value.GetBuffer(iLength), pStart + open.GetLength(), iLength);
	value.ReleaseBuffer(iLength);
	
	value.Replace( L"&quot;", L"\"" );
	value.Replace( L"&lt;",   L"<" );
	value.Replace( L"&gt;",   L">" );
	value.Replace( L"&amp;",  L"&" );

	return (CComBSTR)value;
}

} // namespace XML
